package top.zhangjianyong.utils;

import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import top.zhangjianyong.entity.Photo;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import static org.bytedeco.javacpp.avutil.AV_PIX_FMT_VDPAU_H264;

/**
 * @Author KeXin
 * @Date 2021/2/1 下午3:07
 **/
public class VideoUtil {

    /**
     * base64字符串转换成图片
     *
     * @param imgStr      base64字符串
     * @param imgFilePath 图片存放路径
     */
    private static void base64ToImage(String imgStr, String imgFilePath) {
        try {
            byte[] buffer = new BASE64Decoder().decodeBuffer(imgStr);
            base64ToImage(buffer, imgFilePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void base64ToImage(byte[] buffer, String imgFilePath) {
        try {
            FileOutputStream out = new FileOutputStream(imgFilePath);
            out.write(buffer);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将截取的帧生成文件或者生成base64
     *
     * @param frame
     * @return
     */
    private static String getBase64FromFrame(Frame frame) {
        String imgFormat = "jpg";
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage srcBi = converter.getBufferedImage(frame);
        // 可以选择对截取的帧进行等比例缩放
//        int owidth = srcBi.getWidth();
//        int oheight = srcBi.getHeight();
//        int width = 800;
//        int height = (int) (((double) width / owidth) * oheight);
//        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
//        bi.getGraphics().drawImage(srcBi.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
        try {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            // ImageIO工具类提供了将图片写成文件或者outputStream中
//            ImageIO.write(bi, imgFormat, targetFile);
            ImageIO.write(srcBi, imgFormat, output);
            // 这里需要获取图片的base64数据串，所以将图片写到流里面
            return new BASE64Encoder().encode(output.toByteArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取指定视频的帧并保存为图片至指定目录
     *
     * @param fileUrl    源视频文件Url路径
     * @param stepSecond 每隔几秒取一帧，默认1200s
     * @param count      最多获取几帧
     * @return 返回截取的帧的Base64串列表
     * @throws Exception
     */
    public static void fetchPicFromVideo(String fileUrl, Integer stepSecond, Integer count, PhotoHolder photoHolder) throws Exception {
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(fileUrl);
        ff.setPixelFormat(AV_PIX_FMT_VDPAU_H264);
        fetchPicFromVideo(ff, stepSecond, count, fileUrl, photoHolder);
    }

    /**
     * 获取指定视频的帧并保存为图片至指定目录
     *
     * @param file       源视频文件
     * @param stepSecond 每隔几秒取一帧，默认1200s
     * @param count      最多获取几帧
     * @return 返回截取的帧的Base64串列表
     * @throws Exception
     */
    public static void fetchPicFromVideo(File file, Integer stepSecond, Integer count, PhotoHolder photoHolder) throws Exception {
        FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
        fetchPicFromVideo(ff, stepSecond, count, file.getName(), photoHolder);
    }

    private static void fetchPicFromVideo(FFmpegFrameGrabber ff, Integer stepSecond, Integer count, String fileName, PhotoHolder photoHolder) throws Exception {

        stepSecond = stepSecond == null ? 1200 : stepSecond;
        ff.start();
        long timeLength = ff.getLengthInTime();
        Frame frame = ff.grabImage();
        long startTime = frame.timestamp;
        long timestamp = 0;
        int second = 0;
        int picNum = 0;
        while (timestamp <= timeLength) {
            timestamp = startTime + second * 1000000L;
            //System.out.println(timestamp);
            ff.setTimestamp(timestamp);
            frame = ff.grabImage();
            if (frame != null) {
                if (frame.image != null) {
                    String base64 = getBase64FromFrame(frame);
                    byte[] buffer = new BASE64Decoder().decodeBuffer(base64);
                    Photo photo = new Photo(buffer, timestamp * 1000, 7, 8, fileName);
                    photoHolder.addPhoto(photo);
                    picNum++;
                    if (count != null && picNum == count) {
                        break;
                    }
                }
            }
            second += stepSecond;
        }
        ff.stop();
    }

    public static byte[] base64ToByte(String base64) throws IOException {
        return new BASE64Decoder().decodeBuffer(base64);
    }

    public static void main(String[] args) throws Exception {
        String out = "/Users/zhangjianyong/Downloads/cut";
        String path = "/Users/zhangjianyong/Downloads/output/P220615_123014_124015.264.mp4";
        long start = new Date().getTime();
        File file = new File(path);
        PhotoHolder photoHolder = new PhotoHolder();
        fetchPicFromVideo(file, 1, 0, photoHolder);
        List<Photo> photos = photoHolder.getPhotos();
        System.out.println("开始保存文件");
        System.out.println(photos.size());
        for (Photo photo : photos) {
            ImageIO.write(photo.getImg(), "jpg", new File(out + "/" + photo.getFileName("")));
        }
        System.out.println("done");
        long end = new Date().getTime();
        System.out.println("耗时：" + (end - start));
    }

}


